package com.andova.view

import android.animation.Animator
import android.animation.TypeEvaluator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.drawable.Drawable
import android.os.Build
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.Gravity
import android.view.KeyEvent
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.AutoCompleteTextView
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.ViewCompat
import com.andova.common.R
import kotlin.math.max

/**
 * Created by Administrator on 2019-02-14.
 *
 * @author kzaxil
 * @since 0.0.4
 */
class Search : ViewGroup, TextWatcher, TextView.OnEditorActionListener, ValueAnimator.AnimatorUpdateListener, View.OnFocusChangeListener {
    private var mSearchListener: OnSearchListener? = null

    interface OnSearchListener {
        fun search(s: String)
    }

    fun setOnSearchListener(listener: OnSearchListener) {
        mSearchListener = listener
    }

    override fun onFocusChange(v: View?, hasFocus: Boolean) {
        if (hasFocus && mCollapsed) expand()
    }

    override fun onAnimationUpdate(animation: ValueAnimator?) {
        val element = (animation ?: return).animatedValue as AnimatorElement
        animationUpdate(element)
    }

    override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
        if (actionId == EditorInfo.IME_ACTION_SEARCH) {
            search()
            return true
        }
        return false
    }

    override fun afterTextChanged(s: Editable?) {
        if (TextUtils.isEmpty(s.toString())) {
            if (clear.visibility != View.GONE) clear.visibility = View.GONE
        } else {
            if (clear.visibility != View.VISIBLE) clear.visibility = View.VISIBLE
        }
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
    }

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    }

    private var mText: String? = null

    /**
     * 搜索框是否折叠
     */
    private var mCollapsed = false
    private var percent = 1f
    private var mWidth = -1
    private var mHeight = -1
    private var mAnimator: ValueAnimator? = null
    private var mEndElement: AnimatorElement? = null
    private var mStartElement: AnimatorElement? = null
    private val mEvaluator = AnimatorEvaluator()
    private var clearIconPadding = 0
    private var editPaddingLeft = 0
    private var editPaddingTop = 0
    private var editPaddingRight = 0
    private var editPaddingBottom = 0
    private var parentPaddingLeft = 0
    private var parentPaddingTop = 0
    private var parentPaddingRight = 0
    private var parentPaddingBottom = 0
    private var clearDrawable: Drawable? = null
    private var searchDrawable: Drawable? = null
    private lateinit var clear: ImageView
    private lateinit var search: ImageView
    private lateinit var edit: AutoCompleteTextView

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        init(context, attrs)
    }

    constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr) {
        init(context, attrs)
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
        init(context, attrs)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        measureChildren(widthMeasureSpec, heightMeasureSpec)// 测量子View尺寸信息
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val suggestWidth = MeasureSpec.getSize(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val suggestHeight = MeasureSpec.getSize(heightMeasureSpec)

        when (widthMode) {
            MeasureSpec.AT_MOST, MeasureSpec.UNSPECIFIED -> {
            }
            MeasureSpec.EXACTLY -> {
            }
        }

        var height = edit.textSize.toInt()
        val searchH = searchDrawable?.intrinsicHeight ?: 0
        if (searchH > height) height = searchH
        val clearH = clearDrawable?.intrinsicHeight ?: 0
        if (clearH > height) height = clearH
        when (heightMode) {
            MeasureSpec.AT_MOST, MeasureSpec.UNSPECIFIED -> height += parentPaddingTop + parentPaddingBottom + editPaddingTop + editPaddingBottom
            MeasureSpec.EXACTLY -> height = suggestHeight
        }

        setMeasuredDimension(suggestWidth, height)
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val w = r - l
        val h = b - t
        println("layout -> w:$w,h:$h")
        if (mWidth == -1) mWidth = w
        if (mHeight == -1) mHeight = h
        val searchW = searchDrawable?.intrinsicWidth ?: 0
        val clearW = clearDrawable?.intrinsicWidth ?: 0
        val searchL = searchLeft(w, searchW).toInt()
        search.layout(searchL, parentPaddingTop, searchL + searchW, h - parentPaddingBottom)
        clear.layout(
                w - parentPaddingRight - searchW - clearW - editPaddingRight,
                parentPaddingTop + editPaddingTop,
                w - parentPaddingRight - searchW - editPaddingRight,
                h - parentPaddingBottom - editPaddingBottom
        )
        edit.layout(
                (percent * parentPaddingLeft).toInt(),
                (percent * parentPaddingTop).toInt(),
                editRightForCollapse(w).toInt(),
                h - (percent * parentPaddingBottom).toInt()
        )
    }

    private fun searchLeft(w: Int, searchW: Int): Float {
        return parentPaddingLeft + percent * editPaddingLeft - percent * editPaddingLeft + percent * (w - parentPaddingLeft - parentPaddingRight - searchW)
    }

    private fun editRightForCollapse(w: Int): Float {
        val searchW = searchDrawable?.intrinsicWidth ?: 0
        val searchH = searchDrawable?.intrinsicHeight ?: 0
        if (searchW != searchH) {
            val width = max(searchW, searchH)
        }
        return w - percent * parentPaddingRight - percent * searchW
    }

    private fun init(context: Context, attrs: AttributeSet?) {
        clear = ImageView(context)
        search = ImageView(context)
        edit = AutoCompleteTextView(context)
        edit.addTextChangedListener(this)
        edit.setOnEditorActionListener(this)
        edit.setSingleLine(true)
        edit.imeOptions = EditorInfo.IME_ACTION_SEARCH
        edit.onFocusChangeListener = this
        edit.gravity = Gravity.CENTER_VERTICAL
        search.setOnClickListener { search() }
        clear.setOnClickListener { edit.text.clear() }
        // 注意，这里用的是attachViewToParent，而非addViewInLayout
        attachViewToParent(edit, -1, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
        attachViewToParent(clear, -1, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
        attachViewToParent(search, -1, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))

        val ta = context.obtainStyledAttributes(attrs, R.styleable.Search)
        clearIconPadding = ta.getDimensionPixelOffset(R.styleable.Search_searchClearIconPadding, 0)
        editPaddingLeft = ta.getDimensionPixelOffset(R.styleable.Search_searchEditPaddingLeft, 0)
        editPaddingTop = ta.getDimensionPixelOffset(R.styleable.Search_searchEditPaddingTop, 0)
        editPaddingRight = ta.getDimensionPixelOffset(R.styleable.Search_searchEditPaddingRight, 0)
        editPaddingBottom = ta.getDimensionPixelOffset(R.styleable.Search_searchEditPaddingBottom, 0)
        clearDrawable = ta.getDrawable(R.styleable.Search_searchClearIcon)
                ?: ResourcesCompat.getDrawable(resources, R.drawable.ic_clear_icon, null)
        searchDrawable = ta.getDrawable(R.styleable.Search_searchSearchIcon)
                ?: ResourcesCompat.getDrawable(resources, R.drawable.ic_search_icon, null)
        ViewCompat.setBackground(edit, ta.getDrawable(R.styleable.Search_searchBackground))
        val textSize = ta.getDimensionPixelOffset(R.styleable.Search_searchTextSize, 0)
        if (textSize > 0) edit.textSize = textSize.toFloat()
        val textColor = ta.getColor(R.styleable.Search_searchTextColor, 0)
        if (textColor > 0) edit.setTextColor(ta.getColor(R.styleable.Search_searchTextColor, 0))
        ta.recycle()
        clear.setImageDrawable(clearDrawable)
        search.setImageDrawable(searchDrawable)
        edit.setPadding(editPaddingLeft, editPaddingTop, editPaddingRight + (clearDrawable?.intrinsicWidth
                ?: 0), editPaddingBottom)

        parentPaddingLeft = paddingLeft
        parentPaddingTop = paddingTop
        parentPaddingRight = paddingRight
        parentPaddingBottom = paddingBottom
    }

    private fun hideKeyboard() {
        val imm = (context.getSystemService(Context.INPUT_METHOD_SERVICE)
                ?: return) as InputMethodManager
        if (!imm.isActive) return
        if (windowToken != null) imm.hideSoftInputFromWindow(windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
    }

    inner class AnimatorElement {
        var width: Int = 0
        var height: Int = 0
        var fraction: Float = 1f
    }

    private fun value(fraction: Float, startValue: Int, endValue: Int): Int {
        return (startValue - fraction * (startValue - endValue)).toInt()
    }

    private fun value(fraction: Float, startValue: Float, endValue: Float): Float {
        return if (startValue > endValue) {
            1 - fraction
        } else fraction
    }

    private inner class AnimatorEvaluator : TypeEvaluator<AnimatorElement> {
        override fun evaluate(fraction: Float, startValue: AnimatorElement, endValue: AnimatorElement): AnimatorElement {
            val element = AnimatorElement()
            element.width = value(fraction, startValue.width, endValue.width)
            element.height = value(fraction, startValue.height, endValue.height)
            element.fraction = value(fraction, startValue.fraction, endValue.fraction)
            return element
        }
    }

    private fun initAnimator(shouldAddListener: Boolean) {
        mAnimator = null
        mStartElement = mStartElement ?: AnimatorElement()
        mStartElement?.width = mWidth
        mStartElement?.height = mHeight
        mStartElement?.fraction = 1f
        mEndElement = mEndElement ?: AnimatorElement()
        mEndElement?.width = widthForCollapse()
        mEndElement?.height = heightForCollapse()
        mEndElement?.fraction = 0f

        mAnimator = ValueAnimator.ofObject(mEvaluator, if (mCollapsed) mEndElement else mStartElement, if (mCollapsed) mStartElement else mEndElement)
        if (!shouldAddListener) return
        mAnimator?.addUpdateListener(this)
        mAnimator?.addListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(animation: Animator) {
            }

            override fun onAnimationEnd(animation: Animator) {
            }

            override fun onAnimationCancel(animation: Animator) {}

            override fun onAnimationRepeat(animation: Animator) {}
        })
        mAnimator?.duration = 500L
    }

    private fun widthForCollapse(): Int {
        val searchW = searchDrawable?.intrinsicWidth ?: 0
        val searchH = searchDrawable?.intrinsicHeight ?: 0
        return max(searchW, searchH) + parentPaddingLeft + parentPaddingRight
    }

    private fun heightForCollapse(): Int {
        val searchW = searchDrawable?.intrinsicWidth ?: 0
        val searchH = searchDrawable?.intrinsicHeight ?: 0
        return max(searchW, searchH) + parentPaddingTop + parentPaddingBottom
    }

    fun collapse() {
        if (mCollapsed) return
        initAnimator(true)
        mText = edit.text.toString()
        edit.text.clear()
        mAnimator?.start()
        mCollapsed = true
        clearFocus()
    }

    fun collapse(percent: Float) {
        initAnimator(false)
        mText = edit.text.toString()
        edit.text.clear()
        val element = mEvaluator.evaluate(percent, mStartElement ?: return, mEndElement ?: return)
        animationUpdate(element)
        if (percent <= 0) mCollapsed = false else if (percent >= 1) mCollapsed = true
    }

    fun expand() {
        if (!mCollapsed) return
        initAnimator(true)
        edit.setText(mText)
        mAnimator?.start()
        mCollapsed = false
    }

    private fun animationUpdate(element: AnimatorElement) {
        percent = element.fraction
        layoutParams.width = element.width
        layoutParams.height = element.height
        setPadding(
                (percent * parentPaddingLeft).toInt(),
                (percent * parentPaddingTop).toInt(),
                (percent * parentPaddingRight).toInt(),
                (percent * parentPaddingBottom).toInt()
        )
        requestLayout()
    }

    private fun search() {
        // 当按了搜索之后关闭软键盘
        hideKeyboard()
        mSearchListener?.search(edit.text.toString())
    }
}